package com.youan.log.modules.audit.dataanalysis;

import com.youan.log.common.threadtask.ThreadTaskProgressor;
import com.youan.log.modules.audit.entity.AttackAnalysis;
import com.youan.log.modules.audit.service.IAttackAnalysisService;
import org.springblade.core.tool.utils.SpringUtil;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * 攻击者攻击行为分析
 * @author xsh
 * @return
 */
public class AttackAnalysisDataExtract {
    private static final int loginFailureCountLimit = 10;

    private String date;
    private ThreadTaskProgressor taskProgressor;
    private IP2Region ip2Region;

    private JdbcTemplate jdbcTemplate;
    private IAttackAnalysisService analysisService;

    private List<AttackAnalysis> waitPushToDB;

    public AttackAnalysisDataExtract(String date, ThreadTaskProgressor taskProgressor, IP2Region ip2Region) {
        this.date = date;
        this.taskProgressor = taskProgressor;
        this.ip2Region = ip2Region;

        DataSource dataSource = SpringUtil.getBean(DataSource.class);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.analysisService = SpringUtil.getBean(IAttackAnalysisService.class);

        this.waitPushToDB = new ArrayList<>(300);
    }

    public void extract() {
        List<String> tartgetIPs = getTargetIPs();
        this.taskProgressor.resetProgressTotal(tartgetIPs.size());

        for (String targetIp : tartgetIPs) {
            IP2Region.Region region = this.ip2Region.ip2Region(targetIp);
            AttackAnalysis analysisBean = getAnalysisBean(targetIp, region);
            this.waitPushToDB.add(analysisBean);

            if(this.waitPushToDB.size() >= 5000) {
                pushToDB();
            }
            this.taskProgressor.addProgressNum();
        }

        pushToDB();
    }

    private void pushToDB() {
        this.analysisService.saveBatch(this.waitPushToDB);
        this.waitPushToDB = new ArrayList<>(5000);
    }

    protected AttackAnalysis getAnalysisBean(String attackerIp, IP2Region.Region region) {
        int loginFailureCountTooMuch = getLoginFailureTooMuchNumber(attackerIp);
        //System.out.println("登陆失败次数过多: "+loginFailureCountTooMuch);
        int loginDBTooMuch = getLoginTooMuchDBFailure(attackerIp);
        //System.out.println("登陆失败的数据库过多: "+loginDBTooMuch);
        int attackFunction = getAttackFunction(attackerIp);
        //System.out.println("攻击函数： "+attackFunction);
        int totalNum = getTotalAttackCount(attackerIp);
        //System.out.println("总攻击次数: "+totalNum);
        int loginSuccess = getLongSuccess(attackerIp);

        AttackAnalysis analysis = new AttackAnalysis();
        analysis.setDate(date);
        analysis.setAttackerIp(attackerIp);
        analysis.setAttackTotalCount(totalNum);
        analysis.setAttackSuccessCount(loginSuccess + attackFunction);
        analysis.setLoginFailureTooMuch(loginFailureCountTooMuch);
        analysis.setLoginDbTooMuch(loginDBTooMuch);
        analysis.setAttackFunctionCount(attackFunction);
        analysis.setCountry(region.getCountry());
        analysis.setProvince(region.getProvince());
        analysis.setCity(region.getCity());
        analysis.setIsp(region.getIsp());
        return analysis;
    }

    protected List<String> getTargetIPs() {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT dest_ip FROM ")
                .append(" (")
                // 登陆一台机器失败十次以上
                .append(" SELECT dest_ip FROM log_statistical WHERE visit_date = '").append(date).append("'")
                    .append(" AND issue_type = '10050001084' AND visit_number > 10")
                .append(" union")
                // 登陆多台机器
                .append(" SELECT dest_ip FROM (")
                .append(" SELECT dest_ip, COUNT(distinct source_ip) AS num FROM log_statistical WHERE visit_date='").append(date)
                .append("' AND issue_type = '10050001084' GROUP BY dest_ip having num>3")
                .append(" ) sub_table")
                .append(" union")
                // 存在攻击函数
                .append(" SELECT dest_ip FROM log_statistical WHERE visit_date = '").append(date).append("' AND issue_type in ('10050001948')")
                .append(" union")
                .append(" SELECT source_ip FROM log_statistical WHERE visit_date = '")
                .append(date).append("' AND issue_type in ('10050002295', '10050002065', '10050002066')")
                .append(" ) tmp");
        List<String> result = jdbcTemplate.queryForList(sql.toString(), String.class);
        if(null == result) {
            result = new ArrayList<>();
        }
        return result;
    }

    private int getLongSuccess(String attackerIp) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT count(1) FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND dest_ip = '").append(attackerIp).append("'")
                .append(" AND issue_type = '10050001476'");
        Integer result = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == result) {
            result = 0;
        }
        return result;
    }

    /**
     * 获取总攻击次数
     * @author xsh
     * @date 2021/3/9 16:20
     * @param: attackerIp
     * @return
     */
    private int getTotalAttackCount(String attackerIp) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT SUM(visit_number) FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND source_ip = '").append(attackerIp).append("'");
        Integer num1 = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == num1) {
            num1 = 0;
        }

        sql = new StringBuilder();
        sql.append("SELECT SUM(visit_number) FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND dest_ip = '").append(attackerIp).append("'");

        Integer num2 = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == num2) {
            num2 = 0;
        }

        return num1+num2;
    }

    /**
     *
     *
     * @param attackerIp
     * @return
     */
    private int getAttackFunction(String attackerIp) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT SUM(visit_number) as num FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND dest_ip = '").append(attackerIp).append("'")
                .append(" AND issue_type in ('10050001948')");
        Integer num1 = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == num1) {
            num1 = 0;
        }

        sql = new StringBuilder();
        sql.append("SELECT SUM(visit_number) as num FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND source_ip = '").append(attackerIp).append("'")
                .append(" AND issue_type in ('10050002295', '10050002065', '10050002066')");
        Integer num2 = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == num2) {
            num2 = 0;
        }

        return num1+num2;
    }

    /**
     * 登陆过多数据库失败
     *
     * 对于指定的目标机器，共发生多少次登录失败  检查一个ip登录多个不同的数据库失败
     *
     * @author xsh
     * @date 2021/3/9 15:47
     * @param:
     * @return
     */
    private int getLoginTooMuchDBFailure(String attackerIp) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT COUNT(1) FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND dest_ip = '").append(attackerIp).append("'")
                .append(" AND issue_type = '10050001084'");
        Integer result = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == result) {
            result = 0;
        }
        return result;
    }

    /**
     * 获取attackerIp登陆某一个数据库失败次数过多发生数
     *
     * 获取对指定目标机器，登录失败次数达到阀值的次数，每个ip同一天记为一次 检查一个ip登录一个数据库失败次数过多
     *
     * @author xsh
     * @date 2021/3/9 15:40
     * @param: attackerIp
     * @return
     */
    private int getLoginFailureTooMuchNumber(String attackerIp) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT count(1) FROM log_statistical WHERE visit_date = '").append(date).append("'")
                .append(" AND dest_ip = '").append(attackerIp).append("'")
                .append(" AND issue_type = '10050001084'")
                .append(" AND visit_number > ").append(loginFailureCountLimit);
        Integer result = this.jdbcTemplate.queryForObject(sql.toString(), Integer.class);
        if(null == result) {
            result = 0;
        }
        return result;
    }


}
